对于很多项目,使用既定的约定和使用合理的默认值是需要的,Spring Web MVC现在已经明确支持了约定大于配置。这意味着当你建立一系列命名约定和类似的东西,你可以大幅度减少所需的处理器映射和视图解析器的配置以及ModelAndView的实例等等。这对快速开发有很大的好处,并且当你在生产中更改代码库,它还会给你来带一定程度上的一致性。
约定大于配置的支持主要是针对MVC的三大核心区域:模型,视图和控制器。

22.13.1 The Controller ControllerClassNameHandlerMapping

ControllerClassNameHandlerMapping类是HandlerMapping的实现,约定用来检测请求URL和处理这个请求的Controller实现的映射。
考虑下面这个简单的Controller的实现,尤其注意这个类的名字。

public class ViewShoppingCartController implements Controller {

    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) {
        // the implementation is not hugely important for this example...
    }

}

对应的Spring Web MVC配置文件对应的片段:

<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/>

<bean id="viewShoppingCart" class="x.y.z.ViewShoppingCartController">
    <!-- inject dependencies as required... -->
</bean>

ControllerClassNameHandlerMapping会查找所有定义在应用程序上下文中的处理器(或Controller),并根据它的名字定义映射关系。因此,ViewShoppingCartController映射了/viewshoppingcart*的请求。
让我们通过更多的例子来熟悉这个观点。(注意URL中都是小写,而在Controller类的名字中则是驼峰的规则。) WelcomeController映射/welcome* HomeController映射/home*
IndexController映射/index*
RegisterController映射/register*

对于使用MultiActionController处理器类的情况,映射的规则稍微复杂一些。假设下面的Controller都是MultiActionController的实现: AdminController映射了/admin/* CatalogController映射了/catalog/*

如果你的Controller的名字遵循了xxxController的约定,ControllerClassNameHandlerMapping让你免去了定义和维护一个SimpleUrlHandlerMapping(或类似的工作)。
ControllerClassNameHandlerMapping类继承了AbstractHandlerMapping基类,因此你可以定义HandlerInterceptor实例和像定义HandlerMapping的实现一样定义其他任何你需要的。

22.13.2 The Model ModelMap(ModelAndView)

ModelMap类本质上是Map,可以添加需要在View上展示的对象,它也遵循通用的命名约束。考虑下面的Controller实现;注意对象被添加到ModelAndView而没有指定关联的名称。

public class DisplayShoppingCartController implements Controller {

    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) {

        List cartItems = // get a List of CartItem objects
        User user = // get the User doing the shopping

        ModelAndView mav = new ModelAndView("displayShoppingCart"); <-- the logical view name

        mav.addObject(cartItems); <-- look ma, no name, just the object
        mav.addObject(user); <-- and again ma!

        return mav;
    }
}

ModelAndView类使用了ModelMap类,它是一个自定义的Map实现,当对象被添加时,会自动为添加的对象生成key。检测被添加的对象策略是,像User这样的对象,会使用其简单类名。下面是被添加到ModelMap中的对象所生成的名字的例子。
添加x.y.User实例会生成user的名字。
添加x.y.Registration实例会生成registration的名字。
添加x.y.Foo实例会生成foo的名字。
添加java.util.HashMap实例会生成hashMap的实例。这种情况下,你可能会想要直接指定名字,因为hasmMap不够直观。
* 添加null会引起IllegalArgumentException异常。如果你添加的对象有可能为null,那么你也需要直接指定名字。

什么,没有自动的复数化?
Spring Web MVC的约定大于配置的支持并不支持自动的复数化。也就是说,当你添加了一个ListPersonModelAndView,它不会自动生成people的名字。
这是在一番争议之后决定的,“Principle of Least Surprise”最终赢了。

当添加SetList后,生成名字的策略是检查容器用第一个对象的简单类名并在之后加上List。对于数组来说也是一样的,但是数组没有必要检查内容。下面的一些例子会让你对使用容器时产生名字情况更清晰:
有零个或多个x.y.User元素的x.y.User[]数组产生的名字是userList
有零个或多个x.y.User元素的x.y.Foo[]数组产生的名字是fooList
有一个或多个x.y.User元素的java.util.ArrayList产生的名字userList
有一个或多个x.y.Foo元素的java.util.HashSet产生的名字是fooList
* 一个空的java.util.ArrayList将不会被添加(事实上,addObject(..)的调用本质上是一个no-op)。

22.13.3 Default view name

在没有显示的指定逻辑视图名时RequestToViewNameTranslator接口会检测逻辑的View名。它只有一个实现,DefaultRequestToViewNameTranslator类。
DefaultRequestToViewTranslator映射了请求URL到逻辑视图名,比如:

public class RegistrationController implements Controller {

    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) {
        // process the request...
        ModelAndView mav = new ModelAndView();
        // add data as necessary to the model...
        return mav;
        // notice that no View or logical view name has been set
    }

}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- this bean with the well known name generates view names for us -->
    <bean id="viewNameTranslator"
            class="org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator"/>

    <bean class="x.y.RegistrationController">
        <!-- inject dependencies as necessary -->
    </bean>

    <!-- maps request URLs to Controller names -->
    <bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/>

    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>

注意handleRequest(..)的实现返回的ModelAndView没有设置View或是逻辑视图名。DefaultRequestToViewNameTranslator的任务就是根据请求的URL产生逻辑视图名。对于上面的例子,RegistrationController并且结合使用了ControllerClassNameHandlerMapping的情况,URL为http://localhost/registeration.html会使DefaultRequestToViewNameTranslator产生registeration的视图名。之后这个视图名会被InternalResourceViewResolver解析成/WEB-INF/jsp/registeration.jsp

你不需要显式的定义DefaultRequestToViewNameTranslator。如果你喜欢DefaultRequestToViewNameTranslator的默认设置,你可以依赖Spring Web MVC DispatcherServlet在没有显式配置的情况下,初始化这个类的实例。

当然。如果你需要改变这个默认设置,那么你就要显式配置你自己的DefaultRequestToViewNameTranslator。关于DefaultRequestToViewNameTranslator各种属性的配置详细信息信息,请参考文档。